package net.zoneland.zrdp.framework.aspectj;

import java.util.*;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

import net.zoneland.zrdp.common.annotation.DataScope;
import net.zoneland.zrdp.common.core.domain.BaseEntity;
import net.zoneland.zrdp.common.core.domain.entity.SysRole;
import net.zoneland.zrdp.common.core.domain.entity.SysUser;
import net.zoneland.zrdp.common.core.domain.model.LoginUser;
import net.zoneland.zrdp.common.core.text.Convert;
import net.zoneland.zrdp.common.utils.SecurityUtils;
import net.zoneland.zrdp.framework.security.context.PermissionContextHolder;

/**
 * 数据过滤处理
 *
 * @author zonevue
 */
@Aspect
@Component
public class DataScopeAspect {
    /**
     * 全部数据权限
     */
    public static final String DATA_SCOPE_ALL = "1";

    /**
     * 自定数据权限
     */
    public static final String DATA_SCOPE_CUSTOM = "2";

    /**
     * 部门数据权限
     */
    public static final String DATA_SCOPE_DEPT = "3";

    /**
     * 部门及以下数据权限
     */
    public static final String DATA_SCOPE_DEPT_AND_CHILD = "4";

    /**
     * 仅本人数据权限
     */
    public static final String DATA_SCOPE_SELF = "5";

    /**
     * 数据权限过滤关键字
     */
    public static final String DATA_SCOPE = "dataScope";

    @Before("@annotation(controllerDataScope)")
    public void doBefore(final JoinPoint point, final DataScope controllerDataScope) {
        clearDataScope(point);
        handleDataScope(point, controllerDataScope);
    }

    protected void handleDataScope(final JoinPoint joinPoint, final DataScope controllerDataScope) {
        // 获取当前的用户
        final LoginUser loginUser = SecurityUtils.getLoginUser();
        if (!Objects.isNull(loginUser)) {
            final SysUser currentUser = loginUser.getUser();
            // 如果是超级管理员，则不过滤数据
            if (!Objects.isNull(currentUser) && !currentUser.isAdmin()) {
                final String permission = StringUtils.defaultIfEmpty(controllerDataScope.permission(), PermissionContextHolder.getContext());
                dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(),
                        controllerDataScope.userAlias(), permission);
            }
        }
    }

    /**
     * 数据范围过滤
     *
     * @param joinPoint 切点
     * @param user 用户
     * @param deptAlias 部门别名
     * @param userAlias 用户别名
     * @param permission 权限字符
     */
    public static void dataScopeFilter(final JoinPoint joinPoint, final SysUser user,
                                       final String deptAlias, final String userAlias, final String permission) {
        final StringBuilder sqlString = new StringBuilder();
        final Set<String> conditions = new HashSet<>();
        final boolean hasPermission = StringUtils.isNotEmpty(permission);

        for (final SysRole role : user.getRoles()) {
            final String dataScope = role.getDataScope();
            if (DATA_SCOPE_ALL.equals(dataScope)) {
                sqlString.setLength(0); // 清空
                conditions.add(dataScope);
                break;
            }

            final boolean roleHasPermissions = CollectionUtils.isNotEmpty(role.getPermissions());
            final boolean permissionNotContains = !CollectionUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission));

            if (!DATA_SCOPE_CUSTOM.equals(dataScope) && conditions.contains(dataScope) ||
                (hasPermission && roleHasPermissions && permissionNotContains)) {
                continue;
            }

            // 根据权限不同进行不同的sql 拼接
            processRoleDataScope(dataScope, sqlString, deptAlias, userAlias, user, role);
            conditions.add(dataScope);
        }
        // 多角色情况下，所有角色都不包含传递过来的权限字符，这个时候sqlString也会为空，所以要限制一下,不查询任何数据
        if (conditions.isEmpty()) {
            appendCondition(sqlString, "%s.dept_id = 0", deptAlias);
        }
        // sql 放入BaseEntity 属性中
        setSqlStringToEntity(joinPoint, sqlString);
    }

    /**
     * 处理角色数据权限
     *
     * @param dataScope 数据范围
     * @param sqlString sql 字符串
     * @param deptAlias 部门别名
     * @param userAlias 用户别名
     * @param user 用户
     * @param role 角色
     */
    private static void processRoleDataScope(final String dataScope, final StringBuilder sqlString,
                                             final String deptAlias, final String userAlias,
                                             final SysUser user, final SysRole role) {
        switch (dataScope) {
            case DATA_SCOPE_CUSTOM:
                appendCondition(sqlString, "%s.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = %d )",
                    deptAlias, role.getRoleId());
                break;
            case DATA_SCOPE_DEPT:
                appendCondition(sqlString, "%s.dept_id = %d", deptAlias, user.getDeptId());
                break;
            case DATA_SCOPE_DEPT_AND_CHILD:
                appendCondition(sqlString, "%s.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = %d or find_in_set( %d , ancestors ) )",
                    deptAlias, user.getDeptId(), user.getDeptId());
                break;
            case DATA_SCOPE_SELF:
                if (StringUtils.isNotBlank(userAlias)) {
                    appendCondition(sqlString, "%s.user_id = %d", userAlias, user.getUserId());
                } else {
                    appendCondition(sqlString, "%s.dept_id = 0", deptAlias);
                }
                break;

            default:
                break;
        }
    }

    /**
     * 将拼接的权限SQL 设置到BaseEntity参数中
     * @param joinPoint 切点
     * @param sqlString 拼接的权限SQL
     */
    private static void setSqlStringToEntity(final JoinPoint joinPoint, final StringBuilder sqlString) {
        if (sqlString.length() > 0) {
            final Object params = joinPoint.getArgs()[0];
            if (params instanceof BaseEntity) {
                final BaseEntity baseEntity = (BaseEntity) params;
                baseEntity.getParams().put(DATA_SCOPE, " AND (" + sqlString.substring(4) + ")");
            }
        }
    }


    private static void appendCondition(StringBuilder sqlString, String condition, Object... args) {
        sqlString.append(" OR ").append(String.format(condition, args));
    }

    /**
     * 拼接权限sql前先清空params.dataScope参数防止注入
     */
    private void clearDataScope(final JoinPoint joinPoint) {
        final Object params = joinPoint.getArgs()[0];
        if (!Objects.isNull(params) && params instanceof BaseEntity) {
            final BaseEntity baseEntity = (BaseEntity) params;
            baseEntity.getParams().put(DATA_SCOPE, "");
        }
    }
}
